/*********************************************************************/
char    *pname="smjoin";
char    *usage="233.17.186.20 [-s 162.105.196.100] [-p 1700] [-raw] [-c 10] [-k 1000]";

/*
 * Copyright (C) 2008 CERNET Network Center 
 * smclock/smjoin/smclock6/smjoin6/smsend6/smrec6 
 *
 * Design and coding: 
 * Xing Li <xing@cernet.edu.cn> 
 * Congxiao Bao <congxiao@cernet.edu.cn>
 *
 * Some portions of the code are copied and modified from 
 * mcastClient.c fpont 12/99 pont.net 
 * ssmping package * Copyright (C) 2005  Stig Venaas 
 * 
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 */
/***************************************/
/*
 * Copyright (C) 2005  Stig Venaas <venaas@uninett.no>
 * $Id: ssmping.c,v 1.22 2005/11/29 16:27:26 sv Exp $
 *
 * Contributions:
 * Solaris support by Alexander Gall <gall@switch.ch>
 * Initial Windows support by Nick Lamb <njl@ecs.soton.ac.uk>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 */
/***************************************/

#include <sys/types.h>
#include <sys/time.h>
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>

#include <sys/uio.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netdb.h>

#define SOCKADDR_SIZE(addr) (addr.ss_family == AF_INET ? \
	              sizeof(struct sockaddr_in) : \
	              sizeof(struct sockaddr_in6)) 

void errx(char *, ...);
void err(char *, ...);

void setaddr(struct sockaddr_storage *, struct sockaddr_storage *, const char *, const char *);
char *addr2string(struct sockaddr *, socklen_t);
int name2addrsock(const char *, int *, struct sockaddr_storage *);
void findsrc(struct sockaddr *, struct sockaddr *);

extern int optind;
extern char *optarg;

#ifndef MCAST_JOIN_SOURCE_GROUP
#define MCAST_JOIN_SOURCE_GROUP         46
struct group_source_req {
  uint32_t                gsr_interface; /* interface index */
  struct sockaddr_storage gsr_group;     /* group address */
  struct sockaddr_storage gsr_source;    /* source address */
};
#endif

#define MAX_MSG 100

int     main (argc,argv)
	int     argc;
	char    **argv;
{
        int     ki=0, kfi=0, kfin[10];


    int family, count, us;
    char *group, source[INET6_ADDRSTRLEN], group2[INET6_ADDRSTRLEN], mime2[INET6_ADDRSTRLEN];
    uint32_t intface;
    struct sockaddr_storage name, ucaddr,  grpaddr;
    size_t namelen;

    socklen_t addrlen;
    char msg[MAX_MSG];

    int	sd, bd, rc, nn, clilen;
    struct sockaddr_in servAddr; 
    struct sockaddr_in cliAddr;
    struct ip_mreq mreq;
    struct in_addr mcastAddr; 
    struct hostent *h;
    struct group_source_req gtreq;

    char agroup[100], asource[100];
    int  iport, oport;
    int  mbyte;
    int	num=10;
    int	raw=0;
    int	asm2=0;
    int ssm2=0;
    int	sss=0;
    int	mm;

	int	kk=1;

    strcpy(agroup, "233.17.186.20");
    strcpy(asource, "162.105.196.100");
    iport = 1700;

    for (ki = 1; ki < argc; ki++) {
       if ('-' != argv[ki][0])         		kfin[++kfi] = ki;
       else if (strcmp("-s", argv[ki]) == 0)   	{sss=1; strcpy(asource, argv[++ki]);}
       else if (strcmp("-p", argv[ki]) == 0)   	iport = atoi(argv[++ki]);

       else if (strcmp("-c", argv[ki]) == 0)   	num = atoi(argv[++ki]);
       else if (strcmp("-raw", argv[ki]) == 0) 	raw = 1;

       else if (strcmp("-k", argv[ki]) == 0)   	kk = atoi(argv[++ki]);
       else {
       	fprintf(stderr, "%s: illegal option %s \n", pname, argv[ki]);
       	fprintf(stderr, "Usage: %s %s \n", pname, usage);
       	exit(1);
       }
    }

    if (kfi == 1) {
            strcpy(agroup, argv[ kfin[1] ]); 
	    if (sss == 1) {asm2 = 0; ssm2 = 1;}
	    else          {asm2 = 1; ssm2 = 0;}
    }
    else {
	    if (sss == 1) {asm2 = 0; ssm2 = 0;}
	    else ;
    }

    family = AF_UNSPEC;
    intface = 0;
    count = 0;
    group = NULL;

    /* host info */
    us = name2addrsock(asource, &family, &ucaddr);
    if (us < 0) {printf("error\n"); exit(1);}
    findsrc((struct sockaddr *)&name, (struct sockaddr *)&ucaddr);
    namelen = SOCKADDR_SIZE(name);
    grpaddr = name; 

    /* create socket */
    sd = socket(AF_INET, SOCK_DGRAM, 0); 
    if (sd < 0)	{printf("create socket error\n"); exit(1);}

    /* bind port */
    servAddr.sin_family=AF_INET;
    servAddr.sin_addr.s_addr=htonl(INADDR_ANY); 
    servAddr.sin_port=htons(iport);  
    bd = bind(sd,(struct sockaddr *)&servAddr, sizeof(servAddr));
    if (bd < 0) {printf("bind error\n"); exit(1);} 

    if (asm2 == 1) {
    /* join multicast group (asm) */
    h=gethostbyname(agroup);
    memcpy(&mcastAddr, h->h_addr_list[0], h->h_length);
    mreq.imr_multiaddr.s_addr=mcastAddr.s_addr;
    mreq.imr_interface.s_addr=htonl(INADDR_ANY);
    rc = setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&mreq, sizeof(mreq));
    if (rc < 0)	{printf("join multicast group error\n"); exit(1);} 
    }
 
    /* join multicast group (ssm) */
    if (ssm2 == 1) {
    setaddr(&ucaddr, NULL, NULL, asource);
    setaddr(&grpaddr, NULL, NULL, agroup);
    addrlen = sizeof(struct sockaddr_in);
    memset(&gtreq, 0, sizeof(gtreq));
    memcpy(&gtreq.gsr_source, &ucaddr, addrlen);
    memcpy(&gtreq.gsr_group, &grpaddr, addrlen);
    gtreq.gsr_interface = intface;
    rc = setsockopt(sd, IPPROTO_IP, MCAST_JOIN_SOURCE_GROUP, (char *)&gtreq, sizeof(gtreq));
    if (rc < 0) {printf("join multicast group error (ssm)\n"); exit(1);} 
    }

    /* receive data */
    mm = 0;
    while(1) {
      if (mm++ == num)	exit(0);
      clilen=sizeof(cliAddr);
      nn = recvfrom(sd, msg, MAX_MSG, 0, (struct sockaddr *)&cliAddr, &clilen);
      mbyte = strlen(msg);
      	if (asm2 == 1) {
    		strcpy(source, inet_ntoa(cliAddr.sin_addr));
    		oport = ntohs(cliAddr.sin_port);
    		strcpy(mime2,  addr2string((struct sockaddr *)&name, namelen));
    		/* if ((mm%kk)==0)	printf("ASM: %s#%d [%s#%d] %s  ", source, oport, agroup, iport, mime2); */
    		if ((mm%kk)==0)	printf("ASM: %s#%d [%s#%d]  ", source, oport, agroup, iport);
	}
	else if (ssm2 == 1) {
    		strcpy(source, inet_ntoa(cliAddr.sin_addr));
    		oport = ntohs(cliAddr.sin_port);
    		strcpy(group2, addr2string((struct sockaddr *)&grpaddr, namelen));
    		strcpy(mime2,  addr2string((struct sockaddr *)&name, namelen));
		/* if ((mm%kk)==0)	printf("SSM: %s#%d [%s#%d] %s  ", source, oport, group2, iport, mime2); */
		if ((mm%kk)==0)	printf("SSM: %s#%d [%s#%d]  ", source, oport, group2, iport);
	}
	else {
    		strcpy(source, inet_ntoa(cliAddr.sin_addr));
    		oport = ntohs(cliAddr.sin_port);
    		strcpy(mime2,  addr2string((struct sockaddr *)&name, namelen));
    		/* if ((mm%kk)==0)	printf("UDP: %s#%d [-] %s#%d ", source, oport, mime2, iport); */
    		if ((mm%kk)==0)	printf("UDP: %s#%d [-] %s#%d ", source, oport, mime2, iport);
	}
	if ((mm%kk) == 0) {
      		if (raw == 1)	printf("%d bytes {%s} (%d)\n", mbyte, msg, mm);
		else		printf("%d bytes MSG (%d)\n", mbyte, mm);
	}
    }
}    

/***************************************************************/
void findsrc(struct sockaddr *src, struct sockaddr *dst) {
    int s;
    socklen_t len;
    
    len = dst->sa_family == AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
    s = socket(dst->sa_family, SOCK_DGRAM, 0);
    if (s < 0)
        errx("socket");
       
        /* connect to get sockname */
        if (connect(s, dst, len) < 0)
        errx("connect");

	    if (getsockname(s, src, &len) == -1)
	            errx("getsockname");
		close(s);
}


int name2addrsock(const char *name, int *family, struct sockaddr_storage *addr) {
    struct addrinfo hints, *res;
    int e, s;
  
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = *family;
    hints.ai_socktype = SOCK_DGRAM;

    if ((e = getaddrinfo(name, "4321", &hints, &res))) {
	err("getaddrinfo failed: %s", gai_strerror(e));
	return -1;
    }
    for (; res; res = res->ai_next) {
    	s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
	if (s >= 0) {
	    *family = res->ai_family;
    	    memcpy(addr, res->ai_addr, res->ai_addrlen);
    	    return s;
    	}
	err("socket");
    }
    return -1;
}
	    
void setaddr(struct sockaddr_storage *ss, struct sockaddr_storage *group, const char *addr6, const char *addr4) {
    struct addrinfo hints, *res;
    int e;
 
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = ((struct sockaddr *)ss)->sa_family;
    hints.ai_socktype = SOCK_DGRAM;

    switch (((struct sockaddr *)ss)->sa_family) {
    case AF_INET:
	if ((e = getaddrinfo(addr4, NULL, &hints, &res)))
 	   break;
	((struct sockaddr_in *)ss)->sin_addr = ((struct sockaddr_in *)res->ai_addr)->sin_addr;
	if (group)
	    memcpy(&((struct sockaddr_in *)ss)->sin_addr,
   		   &((struct sockaddr_in *)group)->sin_addr, 3);
	return;
    case AF_INET6:
	if ((e = getaddrinfo(addr6, NULL, &hints, &res)))
	    break;
	((struct sockaddr_in6 *)ss)->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
	if (group)
	    memcpy(&((struct sockaddr_in6 *)ss)->sin6_addr,
    		   &((struct sockaddr_in6 *)group)->sin6_addr, 12);
	return;
    default:
	fprintf(stderr, "Unsupported address family\n");
	exit(1);
	}
/* Only down here if gettaddrinfo failed */
       err("getaddrinfo failed: %s", gai_strerror(e));
}

char *addr2string(struct sockaddr *addr, socklen_t len) {
    static char addr_buf[2][INET6_ADDRSTRLEN];
    static int i = 0;
    i = !i;
    if (getnameinfo(addr, len, addr_buf[i], sizeof(addr_buf[i]),
	    NULL, 0, NI_NUMERICHOST)) {
	    err("getnameinfo");
	return NULL;
    }
    return addr_buf[i];
}

